﻿using System;
using System.Threading;

namespace TSharp.Core.Util
{
    /// <summary>
    /// 会自动释放的锁，可设置等待超时
    /// using (Lock l = new Lock(obj, 10000))
    ///{
    ///    .... 
    ///}
    /// </summary>
    public class Lock : IDisposable
    {
        /// <summary>
        /// 默认超时设置
        /// </summary>
        public static int DefaultMillisecondsTimeout = 15000; // 15S

        private object _obj;

        /// <summary>
        /// 构造 
        /// </summary>
        /// <param name="obj">想要锁住的对象</param>
        public Lock(object obj)
        {
            TryGet(obj, DefaultMillisecondsTimeout, true);
        }

        /// <summary>
        /// 构造
        /// </summary>
        /// <param name="obj">想要锁住的对象</param>
        /// <param name="millisecondsTimeout">超时设置</param>
        public Lock(object obj, int millisecondsTimeout)
        {
            TryGet(obj, millisecondsTimeout, true);
        }

        /// <summary>
        /// 构造
        /// </summary>
        /// <param name="obj">想要锁住的对象</param>
        /// <param name="millisecondsTimeout">超时设置</param>
        /// <param name="throwTimeoutException">是否抛出超时异常</param>
        public Lock(object obj, int millisecondsTimeout, bool throwTimeoutException)
        {
            TryGet(obj, millisecondsTimeout, throwTimeoutException);
        }

        /// <summary>
        /// 获取一个值指示在获取排他锁时是否发生超时，如果为true则未在指定对象上获取排它锁。
        /// <para>
        /// by tangjingbo at 2009-2-24 12:23
        /// </para>
        /// </summary>
        /// <value></value>
        public bool IsTimeout
        {
            get { return _obj == null; }
        }

        #region IDisposable Members

        /// <summary>
        /// 销毁，并释放锁
        /// <para>
        /// by tangjingbo at 2009-2-24 12:23
        /// </para>
        /// </summary>
        public void Dispose()
        {
            if (_obj != null)
            {
                Monitor.Exit(_obj);
                _obj = null;
            }
            GC.SuppressFinalize(this);
        }

        #endregion

        private void TryGet(object obj, int millisecondsTimeout, bool throwTimeoutException)
        {
            if (Monitor.TryEnter(obj, millisecondsTimeout))
            {
                _obj = obj;
            }
            else
            {
                if (throwTimeoutException)
                {
                    throw new TimeoutException("试图获取'" + obj + "'的控制权超时(" + millisecondsTimeout +
                                               "毫秒)，请确认锁对象是否与其他线程冲突，或者查看该代码块执行是否过于耗时。");
                }
            }
        }
    }

    //调用例子：
    //using (Lock l = new Lock(obj, 10000))
    //{
    //    .... 
    //}
    //这样在代码离开 using 块后，会自动执行 Monitor.Exit释放锁。
    //是不是很棒？ :)
}